# /**
# * Copyright (c) 2022 Beijing Jiaotong University
# * PhotLab is licensed under [Open Source License].
# * You can use this software according to the terms and conditions of the [Open Source License].
# *
# * You may obtain a copy of [Open Source License] at: [https://open.source.license/]
# *
# * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# *
# * See the [Open Source License] for more details.
# */
# /**
# * Author: Shihao Xie
# * Created: Apr. 25, 2023
# * Supported by: National Key Research and Development Program of China
# */
from phot_server.core.exception import ParseError
from phot_server.utils.generate_uuid import generate_uuid
from phot_server.utils.toml_parser import parse_toml, save_toml
from phot_server.utils.type_convert import list_to_PhotParameter_dict, list_to_PhotComponent_list, \
    list_to_PhotGraphLink_list, list_to_RenderGraphNode_list, list_to_RenderRoute_list, list_to_RenderTextArea_list, \
    object_to_dict, count_port, dict_to_PhotParameter_dict
from phot_server.rpc.graph_data_type.ttypes import PhotGraph, RenderGraph


def parse_phtg(path: str) -> (PhotGraph, RenderGraph):
    graph_dict = parse_toml(path)
    if graph_dict.get("Graph", None) is None:
        raise ParseError("Photlab Graph File format error. ", path)
    graph = dict_to_PhotGraph(graph_dict)
    render_graph = dict_to_RenderGraph(graph_dict)
    return graph, render_graph


def save_phtg(path: str, graph: PhotGraph, render_graph: RenderGraph):
    save_toml(path, Graph_to_dict(graph, render_graph))

def Graph_to_dict(graph: PhotGraph, render_graph: RenderGraph, is_convert_id=True) -> dict:
    if is_convert_id:
        id_port_dict = {}
        for node in render_graph.nodes:
            id_port_dict[node.nodeId] = count_port(node.ports)

        component_count_dict = {}
        id_map = {}
        for component in graph.components:
            if component_count_dict.get(component.calculationMethod, None) is not None:
                component_count_dict[component.calculationMethod] += 1
            else:
                component_count_dict[component.calculationMethod] = 1
            old_id = component.componentId
            new_id = "{}_{}".format(component.calculationMethod, component_count_dict[component.calculationMethod])
            id_map.update({old_id: new_id})
            component.componentId = new_id

        for node in list(render_graph.nodes):
            if id_map.get(node.nodeId, None) is not None:
                node.nodeId = id_map[node.nodeId]
            else:
                render_graph.nodes.remove(node)

        for edge in graph.links:
            old_id = edge.srcNodeId
            edge.srcNodeId = id_map[old_id]
            edge.srcPortId = int(edge.srcPortId) - id_port_dict[old_id]["input"]
            edge.dstNodeId = id_map[edge.dstNodeId]
            edge.dstPortId = edge.dstPortId
    phot_graph_dict = object_to_dict(graph, obj_list=["PhotGraph", "PhotComponent", "PhotParameter", "PhotGraphLink"])
    render_graph_dict = object_to_dict(render_graph, obj_list=["RenderGraph", "RenderGraphNode", "RenderRect",
                                                               "RenderPort", "RenderRoute", "RenderWayPoint",
                                                               "RenderTextArea"])
    phot_graph_dict.pop("graphPath", None)
    node_map = dict()
    for node in render_graph_dict["nodes"]:
        node_map[node["nodeId"]] = node
    route_map = dict()
    for route in render_graph_dict["routes"]:
        route_map[route["routeId"]] = route

    content = dict()
    content["graphParameters"] = phot_graph_dict["graphParameters"]
    # graph_parameters = phot_graph_dict["graphParameters"]
    # graph_parameter_category = dict()
    # for parameter in graph_parameters:
    #     category = parameter["category"]
    #     if graph_parameter_category.get(category, None) is None:
    #         graph_parameter_category[category] = []
    #     parameter.pop("category", None)
    #     graph_parameter_category[category].append(parameter)
    # content["graphParameters"] = dict()
    # for category, parameters in graph_parameter_category.items():
    #     content["graphParameters"][category] = parameters

    for key, value in phot_graph_dict.items():
        if key not in content.keys():
            content[key] = value

    for component in content["components"]:
        node = node_map[component["componentId"]]
        component["iconName"] = node.get("iconName", None)
        component["rect"] = node["rect"]
        component["ports"] = node["ports"]
    for link in content["links"]:
        route = route_map[link["linkId"]]
        link["points"] = route["path"]
    content["texts"] = render_graph_dict["texts"]
    graph_dict = {"Graph": content}
    return graph_dict

def dict_to_PhotGraph(graph_dict: dict) -> PhotGraph:
    try:
        graph_dict = graph_dict["Graph"]
        return PhotGraph(graphId=graph_dict.get("graphId", None),
                         name=graph_dict["name"],
                         createTime=graph_dict["createTime"],
                         author=graph_dict["author"],
                         description=graph_dict["description"],
                         version=graph_dict["version"],
                         graphParameters=list_to_PhotParameter_dict(graph_dict["graphParameters"])
                            if isinstance(graph_dict["graphParameters"], list)
                               or graph_dict.get("graphParameters", None) is None
                            else dict_to_PhotParameter_dict(graph_dict["graphParameters"]),
                         components=list_to_PhotComponent_list(graph_dict["components"]),
                         links=list_to_PhotGraphLink_list(graph_dict["links"], graph_dict["components"]))
    except KeyError as e:
        raise ParseError("Missing attribute in graph ", e)

def dict_to_RenderGraph(graph_dict: dict) -> RenderGraph:
    try:
        graph_dict = graph_dict["Graph"]
        return RenderGraph(graphId=graph_dict.get("graphId", None),
                           graphName=graph_dict["name"],
                           nodes=list_to_RenderGraphNode_list(graph_dict["components"]),
                           routes=list_to_RenderRoute_list(graph_dict["links"]),
                           texts=list_to_RenderTextArea_list(graph_dict["texts"]))
    except KeyError as e:
        raise ParseError("Missing attribute in graph ", e)